#include "callother.h"

#ifdef CO_COUNT_ALLOCS
static CoType *type_list = 0;

void dump_counts(FILE* f)
{
	CoType *tp;

    fprintf(f, "Dump information of internal types:\n");
    fprintf(f, "          type     alloc'd    freed    max in use\n");
	for (tp = type_list; tp; tp = tp->tp_next)
		fprintf(f, "%15s%10d%10d%10d\n",
			tp->tp_name, tp->tp_allocs, tp->tp_frees,
			tp->tp_maxalloc);
}

void inc_count(CoType *tp)
{
	if (tp->tp_allocs == 0 && tp->tp_next == NULL) {
		/* first time; insert in linked list */
		tp->tp_next = type_list;
		type_list = tp;
	}
	tp->tp_allocs++;
	if (tp->tp_allocs - tp->tp_frees > tp->tp_maxalloc) {
		tp->tp_maxalloc = tp->tp_allocs - tp->tp_frees;
    }
}

void dec_count(CoType *tp)
{
	tp->tp_frees++;
}
#endif

void CoValue_Free(void *self)
{
    CO_FREE(self);
}

long CoValue_Hash(CoValue *self)
{
    CoType *tp = self->v_type;
    if (tp->tp_hash != NULL) {
        return (*tp->tp_hash)(self);
    }
    /* use address as hash value */
    return (long)self;
}

int CoValue_Equal(CoValue *va, CoValue *vb)
{
    if (va == vb) {
        return 1;
    }

    if (va->v_type == vb->v_type) {
        equalfunc fequal = va->v_type->tp_equal;
        if (fequal != NULL)
        {
            return (*fequal)(va, vb);
        }
    }

    return 0;
}

CoValue *CoValue_SelfIter(CoValue *self)
{
	CO_INCREF(self);
	return self;
}

CoValue *CoValue_GetIter(CoValue *self)
{
    CoType *tp = self->v_type;
    getiterfunc f = tp->tp_iter;

    if (f == NULL) {
        /* TODO: [ERROR] not iterable */
        return NULL;
    }
    else {
        CoValue *iter = (*f)(self);
		if (iter != NULL && iter->v_type->tp_iternext == NULL) {
            /* TODO: [ERROR] iter() returned non-iterator */
			CO_DECREF(iter);
			iter = NULL;
		}
		return iter;
	}
}

CoValue *CoIter_Next(CoValue *iter)
{
	assert(iter->v_type->tp_iternext != NULL);
	return (*iter->v_type->tp_iternext)(iter);
}

CoValue *CoValue_Str(CoValue *self)
{
    CoValue *ret;

    if (self == NULL) {
        return CoString_FromString("<NULL>");
    }
    if (COSTRING_CHECK(self)) {
        CO_INCREF(self);
        return self;
    }
    if (self->v_type->tp_str == NULL) {
        char buf[256 + 2];
        snprintf(buf, 256, "<%s at %x>",
                self->v_type->tp_name,
                (unsigned int) self);
        return CoString_FromString(buf);
    }

    ret = (*self->v_type->tp_str)(self);
    if (ret == NULL) {
        char buf[256 + 2];
        snprintf(buf, 256, "<%s at %x>",
                self->v_type->tp_name,
                (unsigned int) self);
        return CoString_FromString(buf);
    }
    assert(COSTRING_CHECK(ret));
    return ret;
}

CoValue *CoLValue_New(CoValue **ptr)
{
    CoLValue *lvalue;

    lvalue = (CoLValue *)CO_MALLOC(sizeof(CoLValue));
    if (lvalue == NULL) {
        /* TODO: [ERROR] no memory */
        return NULL;
    }
    COVALUE_INIT(lvalue, &CoLValue_Type);
    lvalue->l_ptr = ptr;

    return (CoValue *)lvalue;
}

static void lvalue_dealloc(CoValue *self)
{
    assert(COLVALUE_CHECK(self));
    CoValue_Free(self);
}

CoType CoLValue_Type = {
    "lvalue",
	lvalue_dealloc,                 /* tp_dealloc */
	0,                              /* tp_hash */
    0,                              /* tp_equal */
    0,                              /* tp_str */
    0,                              /* tp_iter */
    0,                              /* tp_iternext */
};
